%code top{
    #include <iostream>
    #include <assert.h>
    #include "parser.h"
    extern Ast ast;
    int yylex();
    int yyerror( char const * );
}

%code requires {
    #include "Ast.h"
    #include "SymbolTable.h"
    #include "Type.h"
}

%union {
    int itype;
    char* strtype;
    StmtNode* stmttype;
    ExprNode* exprtype;
    Type* type;
    FuncFParams* Fstype;
    FuncRParams* FRtype;
    IdList* Idlisttype;
    ConstIdList* CIdstype;
}

%start Program
%token <strtype> ID 
%token <itype> INTEGER
%token IF ELSE WHILE BREAK CONTINUE RETURN
%token INT VOID CHAR
%token CONST
%token LPAREN RPAREN LBRACE RBRACE SEMICOLON COMMA
%token ADD SUB MUL DIV MOD OR AND LESS MORE NOTLESS NOTMORE EQUAL UNEQUAL ASSIGN NOT
%token LINECOMMENT COMMENTBEIGN COMMENTELEMENT COMMENTLINE COMMENTEND

%nterm <stmttype> Stmts Stmt AssignStmt BlockStmt IfStmt ReturnStmt DeclStmt FuncDef WhileStmt ConstDeclStmt SingleStmt
%nterm <exprtype> Exp UnaryExp AddExp MulExp Cond LOrExp PrimaryExp LVal RelExp LAndExp 
%nterm <type> Type
%nterm <Idlisttype> Idlist 
%nterm <Fstype> FuncFParams
%nterm <FRtype> FuncRParams
%nterm <CIdstype> ConstIdList

%precedence THEN
%precedence ELSE
%%
Program
    : Stmts {
        ast.setRoot($1);
    }
    ;

//语句序列 
//就算语句或语句拼语句序列
Stmts
    : Stmt {$$=$1;}
    | Stmts Stmt{
        //新建语句序列结点
        $$ = new SeqNode($1, $2);
    }
    ;
//语句
Stmt
    : AssignStmt {$$=$1;}
    | BlockStmt {$$=$1;}
    | IfStmt {$$=$1;}
    | ReturnStmt {$$=$1;}
    | DeclStmt {$$=$1;}
    | ConstDeclStmt {$$=$1;}
    | WhileStmt {$$=$1;}
    | SEMICOLON {$$=new Empty();}
    | BREAK SEMICOLON {$$=new BreakStmt();}
    | CONTINUE SEMICOLON {$$=new ContinueStmt();}
    | SingleStmt {$$=$1;}
    | FuncDef {$$=$1;}
    ;
//左值
LVal
    : ID {
        SymbolEntry *se;
        //在符号表里面找这个ID
        se = identifiers->lookup($1);
        //如果为空说明没找到
        if(se == nullptr)
        {
            fprintf(stderr, "identifier \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        //找到了则new了一个Id类型的结点
        $$ = new Id(se);
        delete []$1;
    }
    ;
//表达式语句  exp; new了一个SingleStmt节点
SingleStmt
    :
    Exp SEMICOLON {
        $$ = new SingleStmt($1);
    }
    ;  
//赋值语句 ident = exp
AssignStmt
    :
    LVal ASSIGN Exp SEMICOLON {
        //new了一个AssignStmt节点，传递给节点的参数是LVal和Exp
        $$ = new AssignStmt($1, $3);
    }
    ;
//语句块 {stms}
BlockStmt
    :   LBRACE //这里涉及符号表的调用，遇到左括号new一张表
        {identifiers = new SymbolTable(identifiers);} 
        Stmts RBRACE 
        {
            //对于语句块，则new一个BlockStmt节点
            $$ = new BlockStmt($3);
            SymbolTable *top = identifiers;
            identifiers = identifiers->getPrev();
            delete top;
        }
    ;
//if语句 分成三种情况
//if ( expr ) stmt 
//if ( expr ) {}
//if ( expr ) stmt else stmt 
IfStmt
    : IF LPAREN Cond RPAREN Stmt %prec THEN {
        //都是new一个IfStmt的结点把对应的expr或者stmt或者空传进去
        $$ = new IfStmt($3, $5);
    }
    | IF LPAREN Cond RPAREN LBRACE RBRACE {
        $$ = new IfStmt($3, new Empty());
    }
    | IF LPAREN Cond RPAREN Stmt ELSE Stmt {
        //有else的要new的是IfElseStmt结点
        $$ = new IfElseStmt($3, $5, $7);
    }
    ;
//while语句
//while ( expr ) stmt
WhileStmt
    :WHILE LPAREN Cond RPAREN Stmt {
        //new一个while结点
        $$ = new WhileStmt($3, $5);
    }
    ;
//return语句 return exp ;
ReturnStmt
    :
    RETURN Exp SEMICOLON{
        $$ = new ReturnStmt($2);
    }
    ;

//变量表达式
Exp
    :
    AddExp {$$ = $1;}
    ;
//条件表达式
Cond
    :
    LOrExp {$$ = $1;}
    ;
//原始表达式 id 或者 整数 或者(exp)
PrimaryExp
    :
    LVal {
        $$ = $1;
    }
    | INTEGER {
        SymbolEntry *se = new ConstantSymbolEntry(TypeSystem::intType, $1);
        //新建一个常数结点
        $$ = new Constant(se);
    }
    | LPAREN Exp RPAREN
    {$$ = $2;}
    ;
//非数组表达式(包括了函数调用)
UnaryExp
    :
    PrimaryExp {$$ = $1;}
    | ID LPAREN RPAREN
    //ID（）
    {
        SymbolEntry *se;
        se = identifiers->lookup($1);
        if(se == nullptr)
        {
            //在符号表中不能找到ID
            fprintf(stderr, "Function \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        //在符号表中能找到ID 这样的形式是函数调用，并且参数为空
        //new一个函数调用的结点
        $$ = new FunctionCall(se, nullptr);
        delete []$1;
    }
    //ID(params)
    | ID LPAREN FuncRParams RPAREN
    {
        SymbolEntry *se;
        se = identifiers->lookup($1);
        if(se == nullptr)
        {
            fprintf(stderr, "Function \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        //在符号表中能找到ID 这样的形式是函数调用，并且参数params
        //new一个函数调用的结点
        $$ = new FunctionCall(se, $3);
        delete []$1;
    }
    //以下是带单目运算符的
    //-exp
    | SUB UnaryExp{
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        //这里和下面都是一样，新建一个singleexpr的结点来表示带单目运算符的表达式
        //传入的三个参数要一一对应
        $$ = new SingleExpr(se, SingleExpr::SUB, $2);
    }
    //!exp
    | NOT UnaryExp{
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new SingleExpr(se, SingleExpr::NOT, $2);
    }
    //+exp
    | ADD UnaryExp{
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new SingleExpr(se, SingleExpr::ADD, $2);
    }
    ;

//乘法级表达式
MulExp
    :
    UnaryExp {$$ = $1;}
    | MulExp MUL UnaryExp
    {
        //乘法级和加法级的都是需要新建二元表达式的结点，包括的符号类型分别有MUL DIV MOD和ADD SUB
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MUL, $1, $3);
    }
    | MulExp DIV UnaryExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::DIV, $1, $3);
    }
    | MulExp MOD UnaryExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MOD, $1, $3);
    }
    ;
//加法级表达式
AddExp
    :
    MulExp {$$ = $1;}
    | AddExp ADD MulExp
    {
        //同上面的乘法的
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::ADD, $1, $3);
    }
    | AddExp SUB MulExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::SUB, $1, $3);
    }
    ;
//关系表达式
RelExp
    :
    AddExp {$$ = $1;}
    | RelExp LESS AddExp
    {
        //和前面的也是一样的，新建二元表达式的结点，包括的符号类型分别有< > <= >= == !=
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::LESS, $1, $3);
    }
    | RelExp MORE AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MORE, $1, $3);
    }
    | RelExp NOTLESS AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::NOTLESS, $1, $3);
    }
    | RelExp NOTMORE AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::NOTMORE, $1, $3);
    }
    | RelExp EQUAL AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::EQUAL, $1, $3);
    }
    | RelExp UNEQUAL AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::UNEQUAL, $1, $3);
    }
    ;
//与运算表达式
LAndExp
    :
    RelExp {$$ = $1;}
    |
    LAndExp AND RelExp
    {
        //还是和上面一模一样 都是二元的 实现的符号有& |
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::AND, $1, $3);
    }
    ;
//或运算表达式
LOrExp
    :
    LAndExp {$$ = $1;}
    |
    LOrExp OR LAndExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::OR, $1, $3);
    }
    ;
//类型
Type
    : INT {
        $$ = TypeSystem::intType;
    }
    | VOID {
        $$ = TypeSystem::voidType;
    }
    | CHAR{
        $$ = TypeSystem::charType;
    }
    ;
//变量的定义语句
DeclStmt
    :
    //可以定义一系列类型相同的变量 放在idlist里
    //新建DeclStmt结点
    Type Idlist SEMICOLON {
        $$ = new DeclStmt($2);
    }
    ;
//常量的定义语句
ConstDeclStmt
    :
    //可以定义一系列类型相同的常量 放在constidlist里
    //新建ConstDeclStmt结点
    CONST Type ConstIdList SEMICOLON{
        $$ = new ConstDeclStmt($3);
    }
    ;


//要定义的ID列表，分为不同的情况
//id
//list,id
//id=exp
//list,id=exp
Idlist
    :
    ID {
        std::vector<Id*> Ids;
        std::vector<AssignStmt*> Assigns;
        //建立一个idlist的结点 传进去id表达式和assign语句的列表
        IdList *temp = new IdList(Ids, Assigns);
        SymbolEntry *se;
        se = new IdentifierSymbolEntry(TypeSystem::intType, $1, identifiers->getLevel());
        identifiers->install($1, se);
        temp -> Ids.push_back(new Id(se));
        $$ = temp;
        delete []$1;
    } 
    |
    Idlist COMMA ID{
        //先把最前面的list放到idlist里面，然后把id页添加进去
        IdList *temp = $1;
        SymbolEntry *se;
        se = new IdentifierSymbolEntry(TypeSystem::intType, $3, identifiers->getLevel());
        identifiers->install($3, se);
        temp -> Ids.push_back(new Id(se));
        $$ = temp;
        delete []$3;
    }
    |
    ID ASSIGN Exp {
        std::vector<Id*> Ids;
        std::vector<AssignStmt*> Assigns;
        IdList *temp = new IdList(Ids, Assigns);
        SymbolEntry *se;
        se = new IdentifierSymbolEntry(TypeSystem::intType, $1, identifiers->getLevel());
        identifiers->install($1, se);
        Id *t = new Id(se);
        temp -> Ids.push_back(t);
        //如果是带赋值语句的情况下，前面都是一样的，把id放到结点的idlist里面，同时也要把赋值语句添加到AssignStmt结点里面
        temp -> Assigns.push_back(new AssignStmt(t, $3));
        $$ = temp;
        delete []$1;
    }
    |
    Idlist COMMA ID ASSIGN Exp {
        IdList *temp = $1;
        SymbolEntry *se;
        se = new IdentifierSymbolEntry(TypeSystem::intType, $3, identifiers->getLevel());
        identifiers->install($3, se);
        Id *t = new Id(se);
        temp -> Ids.push_back(t);
        temp -> Assigns.push_back(new AssignStmt(t, $5));
        $$ = temp;
        delete []$3;
    }
    ;
//要定义的Const类型的ID列表，分为不同的情况
//id=exp
//list,id=exp
ConstIdList
    :
    ID ASSIGN Exp {
        //constid和id是一样的，只不过把id的类型变成constid即可，新建的是ConstIdList结点
        std::vector<ConstId*> ConstIds;
        std::vector<AssignStmt*> Assigns;
        ConstIdList* temp = new ConstIdList(ConstIds, Assigns);
        SymbolEntry* se = new IdentifierSymbolEntry(TypeSystem::intType, $1, identifiers -> getLevel());
        identifiers->install($1, se);
        ConstId *t = new ConstId(se);
        temp -> CIds.push_back(t);
        temp -> Assigns.push_back(new AssignStmt(t, $3));
        $$ = temp;
        delete []$1;
    }
    |
    ConstIdList COMMA ID ASSIGN Exp {
        ConstIdList *temp = $1;
        SymbolEntry *se;
        se = new IdentifierSymbolEntry(TypeSystem::intType, $3, identifiers->getLevel());
        identifiers->install($3, se);
        ConstId *t = new ConstId(se);
        temp -> CIds.push_back(t);
        temp -> Assigns.push_back(new AssignStmt(t, $5));
        $$ = temp;
        delete []$3;
    }
    ;  
//函数调用参数列表
//eg: a
//eg: list,a
FuncRParams
    :
    Exp
    {
        std::vector<ExprNode*> t;
        t.push_back($1);
        FuncRParams *temp = new FuncRParams(t);
        $$ = temp;
    }
    |
    FuncRParams COMMA Exp
    {
        FuncRParams *temp = $1;
        temp -> Exprs.push_back($3);
        $$ = temp;
    }
    ;
//函数定义时的参数
//eg: int a
//eg: list,int a
//eg: int a=b
//eg: list,int a=b
FuncFParams
    :
    Type ID
    {
        std::vector<FuncFParam*> FPs;
        std::vector<AssignStmt*> Assigns;
        //FuncFParams结点 其实和idlist是一样的，可以类比（下面三种也都和idlist一样）
        FuncFParams *temp = new FuncFParams(FPs, Assigns);
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($1, $2, identifiers->getLevel());
        identifiers->install($2, se);
        temp -> FPs.push_back(new FuncFParam(se));
        $$ = temp;
        delete []$2;
    }
    |
    FuncFParams COMMA Type ID
    {
        FuncFParams *temp = $1;
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($3, $4, identifiers->getLevel());
        identifiers->install($4, se);
        temp -> FPs.push_back(new FuncFParam(se));
        $$ = temp;
        delete []$4;
    }
    |
    Type ID ASSIGN Exp
    {
        std::vector<FuncFParam*> FPs;
        std::vector<AssignStmt*> Assigns;
        FuncFParams *temp = new FuncFParams(FPs, Assigns);
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($1, $2, identifiers->getLevel());
        identifiers->install($2, se);
        FuncFParam* t = new FuncFParam(se);
        temp -> FPs.push_back(t);
        temp -> Assigns.push_back(new AssignStmt(t, $4));
        $$ = temp;
        delete []$2;
    }
    |
    FuncFParams COMMA Type ID ASSIGN Exp
    {
        FuncFParams *temp = $1;
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($3, $4, identifiers->getLevel());
        identifiers->install($4, se);
        FuncFParam* t = new FuncFParam(se);
        temp -> FPs.push_back(t);
        temp -> Assigns.push_back(new AssignStmt(t, $6));
        $$ = temp;
        delete []$4;
    }
    ;
//函数定义
//无参数 eg: int fun() {...}
//有参数 eg: int fun(funcfparams){...}
FuncDef
    :
    Type ID LPAREN{
        Type *funcType;
        //在type.h里面定义了functiontype，包括返回类型和函数参数的类型
        funcType = new FunctionType($1,{});
        //函数名是ID
        SymbolEntry *se = new IdentifierSymbolEntry(funcType, $2, identifiers->getLevel());
        identifiers->install($2, se);
        //后面是语句块，新建符号表
        identifiers = new SymbolTable(identifiers);
    }
    RPAREN
    BlockStmt
    {
        SymbolEntry *se;
        se = identifiers->lookup($2);
        assert(se != nullptr);
        //新建FunctionDef结点，由于该情况下没有参数所以传入的是nullptr
        //$6是函数内的语句
        $$ = new FunctionDef(se, nullptr,$6);
        SymbolTable *top = identifiers;
        identifiers = identifiers->getPrev();
        delete top;
        delete []$2;
    }
    | Type ID LPAREN
    {
        Type *funcType;
        funcType = new FunctionType($1,{});
        SymbolEntry *se = new IdentifierSymbolEntry(funcType, $2, identifiers->getLevel());
        identifiers->install($2, se);
        identifiers = new SymbolTable(identifiers);
    }
    FuncFParams RPAREN
    BlockStmt
    {
        SymbolEntry *se;
        se = identifiers->lookup($2);
        assert(se != nullptr);
        //这里有参数了，所以把$5即函数参数传进FunctionDef结点结点
        $$ = new FunctionDef(se, $5 ,$7);
        SymbolTable *top = identifiers;
        identifiers = identifiers->getPrev();
        delete top;
        delete []$2;
    }
    ;
%%

int yyerror(char const* message)
{
    std::cerr<<message<<std::endl;
    return -1;
}